///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
 *	OPCODE - Optimized Collision Detection
 *	Copyright (C) 2001 Pierre Terdiman
 *	Homepage: http://www.codercorner.com/Opcode.htm
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 *	Contains base collider class.
 *	\file		OPC_Collider.h
 *	\author		Pierre Terdiman
 *	\date		June, 2, 2001
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __OPC_COLLIDER_H__
#define __OPC_COLLIDER_H__

	enum CollisionFlag
	{
		OPC_FIRST_CONTACT		= (1<<0),		//!< Report all contacts (false) or only first one (true)
		OPC_TEMPORAL_COHERENCE	= (1<<1),		//!< Use temporal coherence or not
		OPC_CONTACT				= (1<<2),		//!< Final contact status after a collision query
		OPC_TEMPORAL_HIT		= (1<<3),		//!< There has been an early exit due to temporal coherence
		OPC_NO_PRIMITIVE_TESTS	= (1<<4),		//!< Keep or discard primitive-bv tests in leaf nodes (volume-mesh queries)

		OPC_CONTACT_FOUND		= OPC_FIRST_CONTACT | OPC_CONTACT,
		OPC_TEMPORAL_CONTACT	= OPC_TEMPORAL_HIT | OPC_CONTACT,

		OPC_FORCE_DWORD			= 0x7fffffff
	};

	class OPCODE_API Collider
	{
		public:
		// Constructor / Destructor
											Collider();
		virtual								~Collider();

		// Collision report

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/**
		 *	Gets the last collision status after a collision query.
		 *	\return		true if a collision occured
		 */
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		inline_				BOOL			GetContactStatus()			const	{ return mFlags & OPC_CONTACT;							}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/**
		 *	Gets the "first contact" mode.
		 *	\return		true if "first contact" mode is on
		 */
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		inline_				BOOL			FirstContactEnabled()		const	{ return mFlags & OPC_FIRST_CONTACT;					}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/**
		 *	Gets the temporal coherence mode.
		 *	\return		true if temporal coherence is on
		 */
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		inline_				BOOL			TemporalCoherenceEnabled()	const	{ return mFlags & OPC_TEMPORAL_COHERENCE;				}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/**
		 *	Checks a first contact has already been found.
		 *	\return		true if a first contact has been found and we can stop a query
		 */
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		inline_				BOOL			ContactFound()				const	{ return (mFlags&OPC_CONTACT_FOUND)==OPC_CONTACT_FOUND;	}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/**
		 *	Checks there's been an early exit due to temporal coherence;
		 *	\return		true if a temporal hit has occured
		 */
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		inline_				BOOL			TemporalHit()				const	{ return mFlags & OPC_TEMPORAL_HIT;						}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/**
		 *	Checks primitive tests are enabled;
		 *	\return		true if primitive tests must be skipped
		 */
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		inline_				BOOL			SkipPrimitiveTests()		const	{ return mFlags & OPC_NO_PRIMITIVE_TESTS;				}

		// Settings

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/**
		 *	Reports all contacts (false) or first contact only (true)
		 *	\param		flag		[in] true for first contact, false for all contacts
		 *	\see		SetTemporalCoherence(bool flag)
		 *	\see		ValidateSettings()
		 */
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		inline_				void			SetFirstContact(bool flag)
											{
												if(flag)	mFlags |= OPC_FIRST_CONTACT;
												else		mFlags &= ~OPC_FIRST_CONTACT;
											}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/**
		 *	Enable/disable temporal coherence.
		 *	\param		flag		[in] true to enable temporal coherence, false to discard it
		 *	\see		SetFirstContact(bool flag)
		 *	\see		ValidateSettings()
		 */
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		inline_				void			SetTemporalCoherence(bool flag)
											{
												if(flag)	mFlags |= OPC_TEMPORAL_COHERENCE;
												else		mFlags &= ~OPC_TEMPORAL_COHERENCE;
											}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/**
		 *	Enable/disable primitive tests.
		 *	\param		flag		[in] true to enable primitive tests, false to discard them
		 */
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		inline_				void			SetPrimitiveTests(bool flag)
											{
												if(!flag)	mFlags |= OPC_NO_PRIMITIVE_TESTS;
												else		mFlags &= ~OPC_NO_PRIMITIVE_TESTS;
											}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/**
		 *	Validates current settings. You should call this method after all the settings / callbacks have been defined for a collider.
		 *	\return		null if everything is ok, else a string describing the problem
		 */
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		virtual				const char*		ValidateSettings()	= 0;

		protected:
							udword			mFlags;			//!< Bit flags
					const	BaseModel*		mCurrentModel;	//!< Current model for collision query (owner of touched faces)
		// User mesh interface
					const	MeshInterface*	mIMesh;			//!< User-defined mesh interface

		// Internal methods
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/**
		 *	Setups current collision model
		 *	\param		model	[in] current collision model
		 *	\return		TRUE if success
		 */
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		inline_				BOOL			Setup(const BaseModel* model)
											{
												// Keep track of current model
												mCurrentModel = model;
												if(!mCurrentModel)	return FALSE;

												mIMesh = model->GetMeshInterface();
												return mIMesh!=null;
											}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/**
		 *	Initializes a query
		 */
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		virtual inline_		void			InitQuery()		{ mFlags &= ~OPC_TEMPORAL_CONTACT;	}
	};

#endif // __OPC_COLLIDER_H__
